/*
 * Copyright 2012-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.context.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;

import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.ImportPhase;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind;
import org.springframework.boot.context.properties.bind.BindContext;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.util.ObjectUtils;

/**
 * 用于处理导入的不可变 {@link ConfigDataEnvironmentContributor} 树结构。
 *
 * @author Phillip Webb
 * @author Madhura Bhave
 */
class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmentContributor> {

	private static final Predicate<ConfigDataEnvironmentContributor> NO_CONTRIBUTOR_FILTER = (contributor) -> true;

	private final Log logger;

	private final ConfigDataEnvironmentContributor root;

	private final ConfigurableBootstrapContext bootstrapContext;

	/**
	 * 创建一个新的 {@link ConfigDataEnvironmentContributors} 实例。
	 * @param logFactory 日志工厂
	 * @param bootstrapContext 启动上下文
	 * @param contributors 初始贡献者集合
	 */
	ConfigDataEnvironmentContributors(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
									  List<ConfigDataEnvironmentContributor> contributors) {
		this.logger = logFactory.getLog(getClass());
		this.bootstrapContext = bootstrapContext;
		this.root = ConfigDataEnvironmentContributor.of(contributors);
	}

	private ConfigDataEnvironmentContributors(Log logger, ConfigurableBootstrapContext bootstrapContext,
			ConfigDataEnvironmentContributor root) {
		this.logger = logger;
		this.bootstrapContext = bootstrapContext;
		this.root = root;
	}

	/**
	 * 处理所有激活的 contributors 中的导入，并返回一个新的
	 * {@link ConfigDataEnvironmentContributors} 实例。
	 * @param importer 用于导入 {@link ConfigData} 的导入器
	 * @param activationContext 当前的激活上下文，如果上下文尚未创建则为 {@code null}
	 * @return 一个包含所有相关导入已处理完的 {@link ConfigDataEnvironmentContributors} 实例
	 */
	ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
			ConfigDataActivationContext activationContext) {
		// 🧭 获取导入阶段：根据激活上下文确定当前的导入阶段
		ImportPhase importPhase = ImportPhase.get(activationContext);

		// 📜 记录日志：输出当前导入阶段和激活上下文的信息
		this.logger.trace(LogMessage.format("Processing imports for phase %s. %s", importPhase,
				(activationContext != null) ? activationContext : "no activation context"));

		// 🔄 初始化结果：将当前实例赋值给 result，用于后续的链式操作
		ConfigDataEnvironmentContributors result = this;

		// 🔢 初始化计数器：用于统计处理的导入数量
		int processed = 0;

		while (true) {
			// ▶️ 获取下一个待处理的贡献者
			ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);

			// ❌ 如果没有更多贡献者，记录处理数量并返回结果
			if (contributor == null) {
				this.logger.trace(LogMessage.format("Processed imports of %d contributors", processed));
				return result;
			}

			// 🔗 处理未绑定的导入：如果贡献者是未绑定导入，先绑定其属性并更新结果
			if (contributor.getKind() == Kind.UNBOUND_IMPORT) {
				ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(result, activationContext);
				result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
						result.getRoot().withReplacement(contributor, bound));
				continue;
			}

			// 🔍 准备解析上下文：为位置解析器创建上下文
			ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
					result, contributor, activationContext);

			// 📦 准备加载上下文：创建数据加载器上下文
			ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);

			// 📋 获取贡献者的导入列表
			List<ConfigDataLocation> imports = contributor.getImports();

			// 📝 记录正在处理的导入列表
			this.logger.trace(LogMessage.format("Processing imports %s", imports));

			// 📥 解析并加载导入：使用 importer 解析并加载配置
			Map<ConfigDataResolutionResult, ConfigData> imported = importer.resolveAndLoad(activationContext,
					locationResolverContext, loaderContext, imports);

			// 📨 记录加载结果
			this.logger.trace(LogMessage.of(() -> getImportedMessage(imported.keySet())));

			// 🗂️ 更新贡献者树：将加载的导入作为子贡献者添加到当前贡献者中
			ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
					asContributors(imported));
			result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
					result.getRoot().withReplacement(contributor, contributorAndChildren));

			// 🔢 增加处理计数器
			processed++;
		}
	}

	private CharSequence getImportedMessage(Set<ConfigDataResolutionResult> results) {
		if (results.isEmpty()) {
			return "Nothing imported";
		}
		StringBuilder message = new StringBuilder();
		message.append("Imported " + results.size() + " resource" + ((results.size() != 1) ? "s " : " "));
		message.append(results.stream().map(ConfigDataResolutionResult::getResource).collect(Collectors.toList()));
		return message;
	}

	protected final ConfigurableBootstrapContext getBootstrapContext() {
		return this.bootstrapContext;
	}

	private ConfigDataEnvironmentContributor getNextToProcess(ConfigDataEnvironmentContributors contributors,
			ConfigDataActivationContext activationContext, ImportPhase importPhase) {
		for (ConfigDataEnvironmentContributor contributor : contributors.getRoot()) {
			if (contributor.getKind() == Kind.UNBOUND_IMPORT
					|| isActiveWithUnprocessedImports(activationContext, importPhase, contributor)) {
				return contributor;
			}
		}
		return null;
	}

	private boolean isActiveWithUnprocessedImports(ConfigDataActivationContext activationContext,
			ImportPhase importPhase, ConfigDataEnvironmentContributor contributor) {
		return contributor.isActive(activationContext) && contributor.hasUnprocessedImports(importPhase);
	}

	private List<ConfigDataEnvironmentContributor> asContributors(
			Map<ConfigDataResolutionResult, ConfigData> imported) {
		List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>(imported.size() * 5);

		// 🔁 遍历解析出的每个配置项（key 是解析结果，value 是实际的配置数据）
		imported.forEach((resolutionResult, data) -> {
			// 📍 获取配置位置（如 application.yml、classpath:... 等）
			ConfigDataLocation location = resolutionResult.getLocation();

			// 📄 获取配置资源对象
			ConfigDataResource resource = resolutionResult.getResource();

			// 🧩 判断该配置是否是 profile-specific（如 spring.config.activate.on-profile）
			boolean profileSpecific = resolutionResult.isProfileSpecific();

			// 🚫 如果配置数据中没有任何 property sources，说明是空配置位置
			if (data.getPropertySources().isEmpty()) {
				// ➕ 添加一个空位置的 贡献者（标记是否是 profile-specific）
				contributors.add(ConfigDataEnvironmentContributor.ofEmptyLocation(location, profileSpecific));
			}
			else {
				// 🔁 倒序添加 属性源，每个都变成一个 贡献者
				for (int i = data.getPropertySources().size() - 1; i >= 0; i--) {
					contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(location, resource,
							profileSpecific, data, i));
				}
			}
		});

		// 🔒 返回一个不可变列表，确保外部不可修改
		return Collections.unmodifiableList(contributors);
	}

	/**
	 * 返回根贡献者。
	 * @return 根贡献者
	 */
	ConfigDataEnvironmentContributor getRoot() {
		return this.root;
	}

	/**
	 * 返回一个由 contributors 支持的 {@link Binder}。
	 * @param activationContext 激活上下文
	 * @param options 要应用的绑定器选项
	 * @return 一个 binder 实例
	 */
	Binder getBinder(ConfigDataActivationContext activationContext, BinderOption... options) {
		return getBinder(activationContext, NO_CONTRIBUTOR_FILTER, options);
	}

	/**
	 * 返回一个由 contributors 支持的 {@link Binder}。
	 * @param activationContext 激活上下文
	 * @param filter 用于限制 contributors 的过滤器
	 * @param options 要应用的绑定器选项
	 * @return 一个 binder 实例
	 */
	Binder getBinder(ConfigDataActivationContext activationContext, Predicate<ConfigDataEnvironmentContributor> filter,
			BinderOption... options) {
		return getBinder(activationContext, filter, asBinderOptionsSet(options));
	}

	private Set<BinderOption> asBinderOptionsSet(BinderOption... options) {
		return ObjectUtils.isEmpty(options) ? EnumSet.noneOf(BinderOption.class)
				: EnumSet.copyOf(Arrays.asList(options));
	}

	private Binder getBinder(ConfigDataActivationContext activationContext,
			Predicate<ConfigDataEnvironmentContributor> filter, Set<BinderOption> options) {
		boolean failOnInactiveSource = options.contains(BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
		Iterable<ConfigurationPropertySource> sources = () -> getBinderSources(
				filter.and((contributor) -> failOnInactiveSource || contributor.isActive(activationContext)));
		PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(this.root,
				activationContext, null, failOnInactiveSource);
		BindHandler bindHandler = !failOnInactiveSource ? null : new InactiveSourceChecker(activationContext);
		return new Binder(sources, placeholdersResolver, null, null, bindHandler);
	}

	private Iterator<ConfigurationPropertySource> getBinderSources(Predicate<ConfigDataEnvironmentContributor> filter) {
		return this.root.stream().filter(this::hasConfigurationPropertySource).filter(filter)
				.map(ConfigDataEnvironmentContributor::getConfigurationPropertySource).iterator();
	}

	private boolean hasConfigurationPropertySource(ConfigDataEnvironmentContributor contributor) {
		return contributor.getConfigurationPropertySource() != null;
	}

	@Override
	public Iterator<ConfigDataEnvironmentContributor> iterator() {
		return this.root.iterator();
	}

	/**
	 * 贡献者对应的{@link ConfigDataLocationResolverContext}实现。
	 */
	private static class ContributorDataLoaderContext implements ConfigDataLoaderContext {

		private final ConfigDataEnvironmentContributors contributors;

		ContributorDataLoaderContext(ConfigDataEnvironmentContributors contributors) {
			this.contributors = contributors;
		}

		@Override
		public ConfigurableBootstrapContext getBootstrapContext() {
			return this.contributors.getBootstrapContext();
		}

	}

	/**
	 * 贡献者对应的{@link ConfigDataLocationResolverContext}实现。
	 */
	private static class ContributorConfigDataLocationResolverContext implements ConfigDataLocationResolverContext {

		private final ConfigDataEnvironmentContributors contributors;

		private final ConfigDataEnvironmentContributor contributor;

		private final ConfigDataActivationContext activationContext;

		private volatile Binder binder;

		ContributorConfigDataLocationResolverContext(ConfigDataEnvironmentContributors contributors,
				ConfigDataEnvironmentContributor contributor, ConfigDataActivationContext activationContext) {
			this.contributors = contributors;
			this.contributor = contributor;
			this.activationContext = activationContext;
		}

		@Override
		public Binder getBinder() {
			Binder binder = this.binder;
			if (binder == null) {
				binder = this.contributors.getBinder(this.activationContext);
				this.binder = binder;
			}
			return binder;
		}

		@Override
		public ConfigDataResource getParent() {
			return this.contributor.getResource();
		}

		@Override
		public ConfigurableBootstrapContext getBootstrapContext() {
			return this.contributors.getBootstrapContext();
		}

	}

	private class InactiveSourceChecker implements BindHandler {

		private final ConfigDataActivationContext activationContext;

		InactiveSourceChecker(ConfigDataActivationContext activationContext) {
			this.activationContext = activationContext;
		}

		@Override
		public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context,
				Object result) {
			for (ConfigDataEnvironmentContributor contributor : ConfigDataEnvironmentContributors.this) {
				if (!contributor.isActive(this.activationContext)) {
					InactiveConfigDataAccessException.throwIfPropertyFound(contributor, name);
				}
			}
			return result;
		}

	}

	/**
	 * 可用于与{@link ConfigDataEnvironmentContributors#getBinder(ConfigDataActivationContext, BinderOption...)}
	 * 一起使用的绑定器选项。
	 */
	enum BinderOption {

		/**
		 * 如果非活动贡献者包含绑定值，则抛出异常。
		 */
		FAIL_ON_BIND_TO_INACTIVE_SOURCE;

	}

}
